package edu.ucdavis.gwt.gis.client.draw;

import java.util.LinkedList;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

import edu.ucdavis.cstars.client.Graphic;
import edu.ucdavis.cstars.client.MapWidget;
import edu.ucdavis.cstars.client.control.Control;
import edu.ucdavis.cstars.client.control.Position;
import edu.ucdavis.cstars.client.dojo.Color;
import edu.ucdavis.cstars.client.event.DrawEndHandler;
import edu.ucdavis.cstars.client.event.MouseEvent;
import edu.ucdavis.cstars.client.geometry.Geometry;
import edu.ucdavis.cstars.client.layers.GraphicsLayer;
import edu.ucdavis.cstars.client.symbol.SimpleFillSymbol;
import edu.ucdavis.cstars.client.symbol.SimpleLineSymbol;
import edu.ucdavis.cstars.client.symbol.SimpleMarkerSymbol;
import edu.ucdavis.cstars.client.symbol.Symbol;
import edu.ucdavis.cstars.client.toolbars.Draw;
import edu.ucdavis.cstars.client.toolbars.Edit;
import edu.ucdavis.cstars.client.toolbars.Draw.GeometryType;

import edu.ucdavis.gwt.gis.client.DataManager;
import edu.ucdavis.gwt.gis.client.layers.DataLayer;
import edu.ucdavis.gwt.gis.client.layers.FeatureCollectionDataLayer;
import edu.ucdavis.gwt.gis.client.layers.DataLayer.DataLayerType;
import edu.ucdavis.gwt.gis.client.query.QueryToolSettings;
import edu.ucdavis.gwt.gis.client.resources.GadgetResources;

/**
 * Main control for editing and creating features
 * 
 * @author jrmerz
 */
public class DrawControl extends Control {
	
	private static DrawControlUiBinder uiBinder = GWT.create(DrawControlUiBinder.class);
	interface DrawControlUiBinder extends UiBinder<Widget, DrawControl> {}
	
	private FeatureCollectionDataLayer currentLayer = null;
	private LinkedList<FeatureCollectionDataLayer> layers = new LinkedList<FeatureCollectionDataLayer>();
	
	public static final DrawControl INSTANCE = new DrawControl();

	private Draw draw = null;
	private Edit edit = null;
	private boolean isDrawing = false;
	private boolean enabled = false;
	
	//private CanvasMap lineIcon = null;
	//private CanvasMap pointIcon = null;
	//private CanvasMap polyIcon = null;
	private Image lineIcon = new Image(GadgetResources.INSTANCE.line());
	private Image pointIcon = new Image(GadgetResources.INSTANCE.point());
	private Image polyIcon = new Image(GadgetResources.INSTANCE.polygon());
	
	private FocusPanel selectedButton = null;
	private Graphic selectedGraphic = null;

	private EditFeaturePanel editFeaturePanel = null;
	private FeatureCollectionSelectPanel collectionSelectPanel = null;
	
	@UiField SimplePanel outerEditFeaturePanel;
	@UiField HorizontalPanel drawControls;
	@UiField VerticalPanel editControls;
	@UiField FocusPanel addLine;
	@UiField FocusPanel addPoint;
	@UiField FocusPanel addPolygon;
	@UiField FocusPanel done;
	@UiField FocusPanel close;
	@UiField FocusPanel deleteGeometry;
	@UiField FocusPanel finishEdits;
	@UiField HTML collection;
	
	/**
	 * Create the draw control
	 */
	protected DrawControl() {
		initWidget(uiBinder.createAndBindUi(this));
		editFeaturePanel = new EditFeaturePanel();
		collectionSelectPanel = new FeatureCollectionSelectPanel(this);
		collection.setVisible(false);
	}
	
	/**
	 * Enable the control by showing it on the map and setting the tool to a ready state.
	 * 
	 * @param enable - show the control be enabled
	 */
	public void enable(boolean enable) {
		if( enable && QueryToolSettings.INSTANCE.isActive() ) {
			QueryToolSettings.INSTANCE.disable();
		}
		
		this.enabled = enable;
		
		drawControls.setVisible(isEnabled());
		draw.deactivate();
		isDrawing = false;
		setButton(done);
		finishEdits();
		collection.setVisible(false);
		
		if( enable ) {
			DataManager.INSTANCE.getMap().addControl(collectionSelectPanel);
			collectionSelectPanel.update();
		} else {
			DataManager.INSTANCE.getMap().removeControl(collectionSelectPanel);
		}
	}
	
	/**
	 * Add datalayer to the draw control
	 * 
	 * @param layer - layer to be added
	 */
	public void addLayer(FeatureCollectionDataLayer layer) {
		layers.add(layer);
		layer.addClickHandler(
				new edu.ucdavis.cstars.client.event.ClickHandler(){
					@Override
					public void onClick(MouseEvent event) {
						if( !isDrawing && isEnabled() ) {
							
							selectedGraphic = event.getGraphic();
							editControls.setVisible(true);
							
							Geometry.GeometryType type = selectedGraphic.getGeometry().getType();
							if( type == Geometry.GeometryType.POLYLINE || type == Geometry.GeometryType.POLYGON ) {
								edit.activate(Edit.ToolType.EDIT_VERTICES, selectedGraphic);
								editFeaturePanel.editGraphic(selectedGraphic);
							} else if( type == Geometry.GeometryType.POINT ){
								edit.deactivate();
								editFeaturePanel.editGraphic(selectedGraphic);
							}
							editFeaturePanel.show();
						}
					} 
			});
	}
	
	public void setCurrentLayer(FeatureCollectionDataLayer layer) {
		currentLayer = layer;
		setCollectionText(layer.getLabel());
	}
	
	private void setCollectionText(String label) {
		collection.setHTML("Adding to Collection: <span style='color: #777777'>"+label+"</span>");
	}
	
	public FeatureCollectionDataLayer getCurrentLayer() {
		return currentLayer;
	}
	
	/**
	 * Clear the control removing all layers from the map.  Then create
	 * a new blank layer as a default drawing layer.
	 */
	public void clear() {
		for( FeatureCollectionDataLayer layer: layers) {
			layer.removeFromMap(DataManager.INSTANCE.getMap());
		}
		
		layers.clear();
		//currentLayer.clear();
		currentLayer = null;	
	}
	
	// check and see if there is a collection in manager, if so, set if.
	public void update() {
		LinkedList<DataLayer> layers = DataManager.INSTANCE.getDataLayers();
		for( DataLayer dl: layers) {
			if( dl.getType() == DataLayerType.FeatureCollection ) {
				currentLayer = (FeatureCollectionDataLayer) dl;
				break;
			}
		}
		if( currentLayer == null ) {
			currentLayer = new FeatureCollectionDataLayer("DrawLayer");
			DataManager.INSTANCE.getClient().addLayer(currentLayer);
		}
		setCollectionText(currentLayer.getLabel());
		
		collectionSelectPanel.update();
	}
	
	/**
	 * Add a graphic to the currently selected datalayer.
	 * 
	 * @param g - graphics to add.
	 */
	public void add(Graphic g) {
		currentLayer.add(g);
	}
	
	/**
	 * Remove a graphic from the currently selected datalayer.
	 * 
	 * @param g - graphic to remove.
	 */
	public void remove(Graphic g) {
		//currentLayer.remove(g);
		GraphicsLayer gl = g.getLayer();
		if( gl != null ) gl.remove(g);
	}
	
	/**
	 * is this control enabled.
	 * 
	 * @return boolean
	 */
	public boolean isEnabled() {
		return enabled;
	}
	
	/**
	 * Create all the icons
	 */
	private void initIcons() {
		
		// create done
		Image cursor = new Image(GadgetResources.INSTANCE.cursor());
		cursor.getElement().getStyle().setMargin(4, Unit.PX);
		done.add(cursor);
		done.getElement().removeAttribute("tabindex");
		done.setTitle("Select Features");
		
		// create point
		//pointIcon = new CanvasMap(24, 24);
		//CanvasCircleMarker marker = new CanvasCircleMarker(12, 12, 10, "#000000", "#2278DA");
		//pointIcon.addGeometry(marker);
		//pointIcon.redraw();
		//addPoint.add(pointIcon.getCanvas());
		addPoint.add(pointIcon);
		addPoint.getElement().removeAttribute("tabindex");
		//pointIcon.getCanvas().getElement().removeAttribute("tabindex");
		addPoint.setTitle("Add Points");
		
		// create line
		//lineIcon = new CanvasMap(24, 24);
		//CanvasPolyline line = new CanvasPolyline("#2278DA");
		//line.addPoint(1, 22);
		//line.addPoint(10, 2);
		//line.addPoint(20, 15);
		//lineIcon.addGeometry(line);
		//lineIcon.redraw();
		//addLine.add(lineIcon.getCanvas());
		addLine.add(lineIcon);
		addLine.getElement().removeAttribute("tabindex");
		//lineIcon.getCanvas().getElement().removeAttribute("tabindex");
		addLine.setTitle("Add Lines");
		
		// create poly icon
		/*polyIcon = new CanvasMap(24, 24);
		CanvasPolygon poly = new CanvasPolygon("#2278DA", "rgba(34, 120, 218, .6)");
		poly.addPoint(1, 22);
		poly.addPoint(10, 2);
		poly.addPoint(20, 15);
		poly.addPoint(14, 19);
		poly.addPoint(9, 11);
		polyIcon.addGeometry(poly);
		polyIcon.redraw();
		addPolygon.add(polyIcon.getCanvas());*/
		addPolygon.add(polyIcon);
		//polyIcon.getCanvas().getElement().removeAttribute("tabindex");
		addPolygon.getElement().removeAttribute("tabindex");
		addPolygon.setTitle("Add Polygons");
		
		// done editing
		Image delete = new Image(GadgetResources.INSTANCE.trash());
		//HTML delete = new HTML("<div style='color:#2278DA;white-space:nowrap;padding-top:4px'>Delete Geometry</div>");
		deleteGeometry.add(delete);
		deleteGeometry.setTitle("Delete Selected Feature");
		
		// done editing
		HTML finish = new HTML("<div style='color:#2278DA;white-space:nowrap;padding-top:4px'>Finished Editing</div>");
		finishEdits.add(finish);
		
		// close
		HTML finished = new HTML("<div style='color:#2278DA;white-space:nowrap;padding-top:4px'>Finished Drawing</div>");
		close.add(finished);
		close.getElement().removeAttribute("tabindex");
	}
	
	/**
	 * Init all the click handlers
	 */
	private void initHandlers() {
		
		addPoint.addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				setButton(addPoint);
				finishEdits();
				if( isEnabled() ) {
					draw.activate(GeometryType.POINT);
					isDrawing = true;
				}
				collection.setVisible(true);
			}
		});
		
		addLine.addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				setButton(addLine);
				finishEdits();
				if( isEnabled() ) {
					draw.activate(GeometryType.POLYLINE);
					isDrawing = true;
				}
				collection.setVisible(true);
			}
		});
		
		addPolygon.addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				setButton(addPolygon);
				finishEdits();
				if( isEnabled() ) {
					draw.activate(GeometryType.POLYGON);
					isDrawing = true;
				}
				collection.setVisible(true);
			}
		});
		
		done.addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				setButton(done);
				draw.deactivate();
				isDrawing = false;
				collection.setVisible(false);
			}
		});
		
		draw.addDrawEndHandler(new DrawEndHandler() {
			@Override
			public void onDrawEnd(Geometry geometry) {
				Symbol s = null;
				
				if( geometry.getType() == Geometry.GeometryType.POLYLINE ) {
					s = SimpleLineSymbol.create(
						SimpleLineSymbol.StyleType.STYLE_SOLID, 
						Color.create(34, 120, 218, 1), 
						2);
				} else if( geometry.getType() == Geometry.GeometryType.POINT ) {
					s = createStandardMarker();
				} else if ( geometry.getType() == Geometry.GeometryType.POLYGON ) {
					s = SimpleFillSymbol.create(
							SimpleFillSymbol.StyleType.STYLE_SOLID, 
							SimpleLineSymbol.create(
									SimpleLineSymbol.StyleType.STYLE_SOLID,
									Color.create(34, 120, 218, 1),
									1), 
							Color.create(34, 120, 218, .6));
				}
				
				if( s != null ) {
					currentLayer.add(Graphic.create(geometry, s));
				}
			}
		});
		
		deleteGeometry.addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				if( selectedGraphic != null ) {
					remove(selectedGraphic);
					//currentLayer.remove(selectedGraphic);
					edit.deactivate();
					editControls.setVisible(false);
					selectedGraphic = null;
				}
			}
		});
		
		finishEdits.addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				finishEdits();
			}
		});
		
		close.addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				enable(false);
			}
		});
	}
	
	/**
	 * Create the standard marker.
	 * 
	 * @return SimpleMarkerSymbol
	 */
	private SimpleMarkerSymbol createStandardMarker() {
		return SimpleMarkerSymbol.create(
				SimpleMarkerSymbol.StyleType.STYLE_CIRCLE, 
				14, 
				SimpleLineSymbol.create(
						SimpleLineSymbol.StyleType.STYLE_SOLID,
						Color.create(60, 60, 60, 1),
						1),
				Color.create(34, 120, 218, 1)
		);
	}
	
	/**
	 * Finish any edits currently being made to a graphic.
	 */
	private void finishEdits() {
		if( selectedGraphic != null ){
			//resetPoint();
			edit.deactivate();
			editControls.setVisible(false);
			editFeaturePanel.hide();
			selectedGraphic = null;
		}
	}
	
	/**
	 * Set a button to a selected state.
	 * 
	 * @param fp
	 */
	private void setButton(FocusPanel fp) {
		if( selectedButton != null ) selectedButton.setStyleName("drawButton");
		fp.setStyleName("drawButton-selected");
		selectedButton = fp;
	}

	/**
	 * Init the control when it is added to the map.
	 */
	@Override
	public void init(MapWidget map) {
		drawControls.setVisible(false);
		editControls.setVisible(false);
		setPosition(50, 75, Position.TOP_LEFT);
		outerEditFeaturePanel.add(editFeaturePanel);
		
		Draw.Options options = Draw.Options.create();
		options.showTooltips(true);
		draw = Draw.create(map, options);
		
		Edit.Options eOptions = Edit.Options.create();
		eOptions.allowDeleteVertices(true);
		edit = Edit.create(map);
		
		initHandlers();
		initIcons();
		
		update();
	}

}
